Skip to main content

6. 说说你对“三次握手”、“四次挥手”的理解

参考答案:

我们都知道 TCP 是面向连接的,三次握手就是用来建立连接的,四次握手就是用来断开连接的。

三次握手

先上图:

白话文理解

  1. 我能主动给你打·电话吗?

  2. 当然可以啊!那我也能给你打电话吗?

  3. 可以的呢,建⽴连接成功!

核心流程

我们来看一下三次握手的过程:

  • 一开始,客户端和服务端都处于 CLOSED 状态。客户端主动打开连接,服务端被动打开连接,结束CLOSED 状态,开始监听,进入 LISTEN 状态。
  1. 第一次握手(SYN)

    • 客户端向服务器发送一个 SYN(同步序列号)报文,表明客户端希望发起连接。这是建立连接的请求。
    • 在这个报文中,客户端选择一个随机的初始序列号seq = x,并将这个序列号放在 SYN 报文中。

    状态变化:客户端进入 SYN_SENT 状态。

  2. 第二次握手(SYN-ACK)

    • 服务器收到客户端的 SYN 报文后,服务器知道了客户端希望建立连接,于是服务器向客户端发送一个 SYN-ACK 报文。这个报文有两个作用:
      • 确认(ACK)客户端的 SYN 报文,确认号ack = x + 1表示已经收到客户端的 SYN 并准备建立连接。
      • 服务器自己也发送一个 SYN 报文,表示希望与客户端建立连接,并且指定自己的初始序列号seq = y

    状态变化:服务器进入 SYN_RECEIVED 状态。

  3. 第三次握手(ACK)

    • 客户端收到服务器的 SYN-ACK 报文后,知道服务器已同意建立连接。客户端再发送一个确认报文 ACK 给服务器,确认号ack = y + 1,表示已收到服务器的 SYN 报文,双方可以正式传输数据。
    • 这个报文可以携带数据,也可以不携带数据。

    状态变化:客户端进入 ESTABLISHED 状态,服务器收到这个 ACK 报文后也进入 ESTABLISHED 状态。

三次握手的目的

  1. 确认双方发送和接收能力:通过三次握手,客户端和服务器双方都确认对方可以接收和发送数据。
  2. 防止重复连接的建立:三次握手可以防止旧的连接请求报文突然又传送到服务器,导致错误的连接建立。
  3. 同步初始序列号:确保双方都同步好序列号,为后续的可靠数据传输奠定基础。

那么为什么要三次握手呢?两次不行吗?

  • 为了防止服务器端开启一些无用的连接增加服务器开销
  • 防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

由于网络传输是有延时的(要通过网络光纤和各种中间代理服务器),在传输的过程中,比如客户端发起了 SYN=1 的第一次握手。

如果服务器端就直接创建了这个连接并返回包含 SYN、ACK 和 Seq 等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直没有接收到服务器返回的数据包。

如果没有第三次握手告诉服务器端客户端收的到服务器端传输的数据的话,服务器端是不知道客户端有没有接收到服务器端返回的信息的。服务端就认为这个连接是可用的,端口就一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。

这样一来,就会有很多无效的连接端口白白地开着,导致资源的浪费。

这个过程可理解为:

还有一种情况是已经失效的客户端发出的请求信息,由于某种原因传输到了服务器端,服务器端以为是客户端发出的有效请求,接收后产生错误。

所以我们需要“第三次握手”来确认这个过程:

通过第三次握手的数据告诉服务端,客户端有没有收到服务器“第二次握手”时传过去的数据,以及这个连接的序号是不是有效的。若发送的这个数据是“收到且没有问题”的信息,接收后服务器就正常建立 TCP 连接,否则建立 TCP 连接失败,服务器关闭连接端口。由此减少服务器开销和接收到失效请求发生的错误。

数据传输

四次挥手

白话文理解

  1. 我们分⼿吧

  2. 收到分⼿的信息

  3. 好吧,分就分吧

  4. ⾏,那就到这⾥了

为了防⽌最终的 ACK 丢失,发送 ACK 后需要等待⼀段时间,因为如果丢包服务端需要重新发送 FIN 包,如果客户端已经 closed ,那么服务端会将结果解析成错误。 从⽽在⾼并发⾮⻓连接的场景下会有⼤量端⼝被占⽤

上图是客户端主动关闭连接

“四次挥手”是指在 TCP(Transmission Control Protocol)协议中,客户端与服务器之间断开连接时的过程。与“三次握手”建立连接相对应,四次挥手用于优雅地终止连接,确保所有数据都已经成功传输并且双方都明确连接已经关闭。

四次挥手的过程

  1. 第一次挥手(FIN)

    • 主动关闭连接的一方(通常是客户端,但也可以是服务器)发送一个 FIN(Finish)报文,表示它已经完成数据传输,想要关闭连接。
    • FIN 报文携带的序列号seq = u

    状态变化:发送 FIN 报文的一方进入 FIN_WAIT_1 状态。

  2. 第二次挥手(ACK)

    • 被动关闭连接的一方(接收到 FIN 的一方)收到 FIN 报文后,发送一个 ACK(确认)报文,表示已经收到 FIN 报文,确认号ack = u + 1

    状态变化:发送 ACK 报文的一方进入 CLOSE_WAIT 状态,而主动关闭连接的一方在收到 ACK 报文后进入 FIN_WAIT_2 状态。

  3. 第三次挥手(FIN)

    • 被动关闭连接的一方在发送 ACK 后,可能还有数据要发送。完成数据传输后,它也会发送一个 FIN 报文,表示它的数据已经发送完毕,可以关闭连接了。
    • 这个 FIN 报文的序列号为seq = v

    状态变化:被动关闭连接的一方进入 LAST_ACK 状态。

  4. 第四次挥手(ACK)

    • 主动关闭连接的一方收到对方的 FIN 报文后,发送一个 ACK 报文,确认号ack = v + 1,表示已经确认对方关闭连接的请求。
    • 发送完 ACK 报文后,主动关闭连接的一方进入 TIME_WAIT 状态,需要等待一段时间(通常是 2 倍的 MSL,Maximum Segment Lifetime,报文最大生存时间),以确保最后的 ACK 报文能够被对方收到,然后彻底关闭连接。

    状态变化:对方在收到这个 ACK 报文后,进入 CLOSED 状态,连接关闭。主动关闭连接的一方在TIME_WAIT状态下,等待超时后进入 CLOSED 状态。

为什么需要四次挥手?

  • 全双工关闭:TCP 连接是全双工的,即数据可以双向传输。关闭连接时,必须确保双向的数据传输都已完成,因此需要四次挥手。
  • FIN 和 ACK 分离:与建立连接的三次握手不同,关闭连接时,发送 FIN 和 ACK 报文的时机和逻辑不同。在接收到 FIN 后,可能还需要处理剩余的数据,因此 ACK 和 FIN 的发送是分开的。

TIME_WAIT 状态的重要性

  • 防止丢包:在最后的 ACK 报文丢失的情况下,如果没有 TIME_WAIT 状态,对方可能会重发 FIN 报文。TIME_WAIT 状态保证了 ACK 能够被重新发送,确保对方能够顺利进入 CLOSED 状态。
  • 避免旧连接干扰:TIME_WAIT 状态有助于防止在网络中残留的旧连接数据包影响新的连接。

为什么客户端在 TIME-WAIT 阶段要等 2MSL?

为的是确认服务器端是否收到客户端发出的 ACK 确认报文,当客户端发出最后的 ACK 确认报文时,并不能确定服务器端能够收到该段报文。

所以客户端在发送完 ACK 确认报文之后,会设置一个时长为 2MSL 的计时器。

MSL 指的是 Maximum Segment Lifetime:一段 TCP 报文在传输过程中的最大生命周期。

2MSL 即是服务器端发出为 FIN 报文和客户端发出的 ACK 确认报文所能保持有效的最大时长。

服务器端在 1MSL 内没有收到客户端发出的 ACK 确认报文,就会再次向客户端发出 FIN 报文:

  • 如果客户端在 2MSL 内,再次收到了来自服务器端的 FIN 报文,说明服务器端由于各种原因没有接收到客户端发出的 ACK 确认报文。

客户端再次向服务器端发出 ACK 确认报文,计时器重置,重新开始 2MSL 的计时。

  • 否则客户端在 2MSL 内没有再次收到来自服务器端的 FIN 报文,说明服务器端正常接收了 ACK 确认报文,客户端可以进入 CLOSED 阶段,完成“四次挥手”。

所以,客户端要经历时长为 2SML 的 TIME-WAIT 阶段;这也是为什么客户端比服务器端晚进入 CLOSED 阶段的原因。